/*
 *  SampleOsax.cp
 *
 *  Copyright (c) 2005 Satimage. All rights reserved.
 *
 */

#include <Carbon/Carbon.h>
#include <Osax.h>
#include "AEDescUtils.h"
#include <unistd.h>
#include <deque>
#include <Phidget21/phidget21.h>

typedef  int (*ReaderFunction)(CPhidgetInterfaceKitHandle phid, int index, int *inputState);
typedef  int (*CountSensorFunction)(CPhidgetInterfaceKitHandle phid, int * index);
typedef  int (*IntPropertyFunction)(CPhidgetHandle phid, int * index);
typedef	 int (*IKCallbackFunction)(CPhidgetInterfaceKitHandle phid, void *userPtr, int index, int outputState);
typedef  int (*IKCallbackSetterFunction)(CPhidgetInterfaceKitHandle phid,IKCallbackFunction fn, void *userPtr);

EventDescription gEventDescriptionList[]={
{'PIKI','Open',0},
{'PIKI','ReAn',1},
{'PIKI','ReDi',2},
{'PIKI','RRAn',3},
{'PIKI','ReED',4},
{'PIKI','ReDD',5},
{'PIKI','ReOs',6},
{'PIKI','Clos',9},
{'PIKI','Stat',10},
{'PIKI','Vers',11},
{'PIKI','OuCo',12},
{'PIKI','SeCo',13},
{'PIKI','InCo',14},

{'PIKI','SeOT',15},
{'PIKI','SeIT',16},
{'PIKI','SeST',17},
{'PIKI','RaTi',18},
};


int gEventDescriptionCount=sizeof(gEventDescriptionList)/sizeof(EventDescription);


typedef struct _sIKStructure {
	CPhidgetInterfaceKitHandle ifkit;
	ProcessSerialNumber		psn;
	int serial;
	long id;
	bool inUse;
} IKStructure;

static long gIKIdentifier = 0;
std::deque<IKStructure*> gIKStructure;

OSErr IKStructureToAEStream(AEStreamRef  ref, IKStructure * s){
	OSErr err = 0;
	AEStreamOpenDesc(ref,'PIKs');
	AEStreamWriteData(ref,s,sizeof(IKStructure));
	AEStreamCloseDesc(ref);
	return err;
}

OSErr IKStructureToDesc(AEDesc* d, IKStructure *s){
    AEStreamRef ref = 0;
	ref = AEStreamOpen();
	IKStructureToAEStream(ref,s);
	AEStreamClose(ref,d);
    return noErr;
}

OSErr IKStructureFromAppleEvent(const AppleEvent * theEvent, DescType type,IKStructure *s){
	AEDesc desc = {typeNull,NULL};
	unsigned int i = 0;
	
	OSErr err = AEGetParamDesc(theEvent, type, 'PIKs', &desc);
	if(err == noErr) {
		AEGetDescData(&desc,s,sizeof(IKStructure));
		AEDisposeDesc(&desc);
		/* check that the object still exists !*/
		for (i = 0 ; i < gIKStructure.size() ; i++){
			if (gIKStructure[i]->id == s->id){
				s = gIKStructure[i];
				return noErr;
			}
		}
	}
	return -1;
}




int IKCoreNotify(IKStructure * s, int sensorID,int index, int sensorValue, int type){
	AEDescList params = {typeNull,NULL};
	AECreateList(NULL,0,false, &params);
	AEPutPtr(&params, 0, typeSInt16,&sensorID, sizeof(sensorID));
	AEPutPtr(&params, 0, typeSInt16,&index, sizeof(index));
	AEPutPtr(&params, 0, typeSInt16,&sensorValue, sizeof(sensorValue));
	AEPutPtr(&params, 0, typeSInt16,&type, sizeof(type));
	RawNotifySmile(NULL, &params);
	AEDisposeDesc(&params);
	return noErr;
}


int IKOutputChanged(CPhidgetInterfaceKitHandle phid, void *userPtr, int index, int sensorValue){
	IKStructure * s = (IKStructure *)userPtr;
	IKCoreNotify(s, s->serial,index,sensorValue,2);
	return 0;
}

int IKInputChanged(CPhidgetInterfaceKitHandle phid, void *userPtr, int index, int sensorValue){
	IKStructure * s = (IKStructure *)userPtr;
	IKCoreNotify(s, s->serial,index,sensorValue,0);
	return 0;
}

int IKSensorChanged(CPhidgetInterfaceKitHandle phid, void *userPtr, int index, int sensorValue){
	IKStructure * s = (IKStructure *)userPtr;	
	IKCoreNotify(s,s->serial,index,sensorValue,1);
	return 0;
}

OSErr PIKCreateConnection(const AppleEvent* theEvent, AppleEvent* reply){
	OSErr err = noErr;
	int serial = -1;
	int port = 5001;
	char password[128];
	char host[128];
	
	memset(password, 0,sizeof(password));
	memset(host, 0,sizeof(host));
	
	AEDesc desc = {typeNull, NULL};
	AEDesc desc2 = {typeNull, NULL};
	AEDesc desc3 = {typeNull, NULL};
	AEDesc desc4 = {typeNull, NULL};
	
	err = AEGetParamDesc(theEvent, '----', typeSInt16, &desc);
	if(err == noErr) {
		err = AEGetDescData(&desc,&serial, sizeof(serial)); 
		AEDisposeDesc(&desc);
	}
	
	err = AEGetParamDesc(theEvent, 'Popo', typeSInt16, &desc2);
	if(err == noErr) {
		err = AEGetDescData(&desc2,&port, sizeof(port)); 
		AEDisposeDesc(&desc2);
	}
	
	err = AEGetParamDesc(theEvent, 'Popa', typeUnicodeText, &desc3);
	if(err == noErr) {
		err = AEGetDescData(&desc3,&password, sizeof(password)); 
		AEDisposeDesc(&desc3);
	}
	
	err = AEGetParamDesc(theEvent, 'POho', typeText, &desc4);
	if(err == noErr) {
		err = AEGetDescData(&desc4,&host, sizeof(host)); 
		AEDisposeDesc(&desc4);
	}
	
	IKStructure * s = (IKStructure *)malloc(sizeof(IKStructure));
	memset(s,0,sizeof(IKStructure));
	
	if (s != NULL && s->ifkit == NULL){
		if (CPhidgetInterfaceKit_create(&s->ifkit) == EPHIDGET_OK){
			if (err == noErr){
				CPhidget_openRemoteIP((CPhidgetHandle)s->ifkit, serial,host,port,(strlen(password) == 0)?NULL:password);
			} else {
				CPhidget_open((CPhidgetHandle)s->ifkit,serial);
			}
			err = noErr;
			if (CPhidget_waitForAttachment((CPhidgetHandle)s->ifkit,3000) == EPHIDGET_OK){
				
				OSErr anErr = noErr;
				DescType actualType;
				Size actualSize;
				
				CPhidgetInterfaceKit_setRatiometric(s->ifkit,PTRUE);
				CPhidget_getDeviceVersion((CPhidgetHandle)s->ifkit,&s->serial);
				AEDesc res = {typeNull,NULL};
				gIKStructure.push_back(s);
				s->id = gIKIdentifier;
				
				anErr = AEGetAttributePtr(theEvent, keyAddressAttr, typeProcessSerialNumber,&actualType,(Ptr) &(s->psn), sizeof(s->psn), &actualSize);

				/*
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,1,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,2,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,3,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,4,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,5,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,6,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,7,1);
				CPhidgetInterfaceKit_setSensorChangeTrigger(s->ifkit,8,1);
				CPhidgetInterfaceKit_set_OnSensorChange_Handler(s->ifkit,IKSensorChanged,s);
				CPhidgetInterfaceKit_set_OnInputChange_Handler(s->ifkit,IKInputChanged,s);											
				CPhidgetInterfaceKit_set_OnOutputChange_Handler(s->ifkit,IKOutputChanged,s);
				*/
				
				IKStructureToDesc(&res,s);
				AEPutKeyDesc(reply,keyDirectObject,&res);
			} else {
				err = -2;
			}
		} else {
			err = -3;
		}
	} else {
		err = -1;
	}
	if (err !=noErr){
		delete s;
	}
    return err;
}


OSErr PIKReadInput(const AppleEvent* theEvent, AppleEvent* reply, ReaderFunction readingFunction, CountSensorFunction coutingFunction){
	OSErr err = noErr;
	short io = 0;	
	
	IKStructure s;
	memset(&s,0,sizeof(s));
	IKStructureFromAppleEvent(theEvent,'PIKo',&s);
	
	AEDesc desc = {typeNull, NULL};
	err = AEGetParamDesc(theEvent, '----', typeSInt16, &desc);
	if(err == noErr) {
		err = AEGetDescData(&desc,&io, sizeof(io)); 
		AEDisposeDesc(&desc);
	}
	
	if (err == noErr){
		if (s.ifkit != NULL){
			int value = 0;
			AEDesc desc = {typeNull,NULL};
			readingFunction(s.ifkit,io, &value);
			AEDescFromInt(&desc, value);
			AEPutKeyDesc(reply,keyDirectObject,&desc);
			AEDisposeDesc(&desc);
		} else {
			err = -1;
		}		
	} else if(err == errAEDescNotFound) {
		int nbSensors = 0;
		AEDescList values = {typeNull,NULL};
		AECreateList(NULL, 0, false, &values);
		coutingFunction(s.ifkit, &nbSensors);
		for(io = 0 ; io<nbSensors ; io++){
			int value = 0;
			readingFunction(s.ifkit,io, &value);
			if (value == PUNK_INT) value = 0;
			AEPutPtr(&values,0,typeSInt16,&value,sizeof(value));			
		}
		AEPutKeyDesc(reply,keyDirectObject,&values);
		AEDisposeDesc(&values);
		err = noErr;
	}
	
	return err;
}

OSErr PIKChangeOutputState(const AppleEvent* theEvent, AppleEvent* reply, bool state){
	OSErr err = noErr;
	int io = 0;	
	
	IKStructure s;
	memset(&s,0,sizeof(s));
	IKStructureFromAppleEvent(theEvent,'PIKo',&s);
	
	AEDesc desc = {typeNull, NULL};
	err = AEGetParamDesc(theEvent, '----', typeSInt16, &desc);
	if(err == noErr) {
		err = AEGetDescData(&desc,&io, sizeof(io)); 
		AEDisposeDesc(&desc);
	}
	
	if (s.ifkit != NULL){
		CPhidgetInterfaceKit_setOutputState(s.ifkit,io, state);
	} else {
		err = -1;
	}
	return err;
}

OSErr PIKCloseConnection(const AppleEvent* theEvent, AppleEvent* reply){
	OSErr err = noErr;
	IKStructure s;
	memset(&s,0,sizeof(s));
	IKStructureFromAppleEvent(theEvent,'----',&s);
	if (s.ifkit != NULL){
		CPhidget_close((CPhidgetHandle)s.ifkit);
		CPhidget_delete((CPhidgetHandle)s.ifkit);
		s.ifkit = NULL;
	} else {
		err = -1;
	}
	return err;
}

OSErr PIKGetIntProperty(const AppleEvent* theEvent, AppleEvent* reply, IntPropertyFunction intPropetyReadingFunction){
	OSErr err = noErr;
	IKStructure s;
	memset(&s,0,sizeof(s));
	IKStructureFromAppleEvent(theEvent,'----',&s);
	if (s.ifkit != NULL){
		int value = 0;
		AEDesc desc = {typeNull,NULL};
		intPropetyReadingFunction((CPhidgetHandle)s.ifkit, &value);
		AEDescFromInt(&desc, value);
		AEPutKeyDesc(reply,keyDirectObject,&desc);
		AEDisposeDesc(&desc);
	} else {
		err = -1;
	}
	return err;
}

OSErr PIKSetRatiometric(const AppleEvent* theEvent, AppleEvent* reply){
	OSErr err = noErr;
	bool b = false;
	IKStructure s;
	memset(&s,0,sizeof(s));
	IKStructureFromAppleEvent(theEvent,'PIKo',&s);
	
	AEDesc desc = {typeNull, NULL};
	err = AEGetParamDesc(theEvent, '----', typeTrue, &desc);
	if(err == noErr) {
		b = PTRUE;
		AEDisposeDesc(&desc);
	} else {
		b = PFALSE;
	}
	
	if (s.ifkit != NULL){
		CPhidgetInterfaceKit_setRatiometric(s.ifkit, b);
		usleep(100000);
		err = noErr;
	} else {
		err = -1;
	}
	return err;
}


OSErr PIKSetTriggerHandler(const AppleEvent* theEvent, AppleEvent* reply, IKCallbackSetterFunction callbackSetter,IKCallbackFunction callback){
	OSErr err = noErr;
	return err;
}

pascal OSErr OsaxEventHandler(const AppleEvent* message, AppleEvent* reply, long refCon){
    switch(refCon){
			
			/* Connecting and disconnecting */
		case 0	 : return PIKCreateConnection(message, reply);
		case 9	 : return PIKCloseConnection(message,reply);
			
			/* reading input values (raw, digital & analogic values …)*/	
		case 1   : return PIKReadInput(message, reply,CPhidgetInterfaceKit_getSensorValue,CPhidgetInterfaceKit_getSensorCount);	
		case 2   : return PIKReadInput(message,reply,CPhidgetInterfaceKit_getInputState,CPhidgetInterfaceKit_getOutputCount);
		case 3   : return PIKReadInput(message,reply,CPhidgetInterfaceKit_getSensorRawValue,CPhidgetInterfaceKit_getSensorCount);
		case 6   : return PIKReadInput(message,reply,CPhidgetInterfaceKit_getOutputState,CPhidgetInterfaceKit_getOutputCount);
			
			/* enabling/disabling digital output */	
		case 4   : return PIKChangeOutputState(message,reply,true);
		case 5   : return PIKChangeOutputState(message,reply,false);
			
			/* reading poperties from the phidget */
		case 10  : return PIKGetIntProperty(message,reply,CPhidget_getDeviceStatus);
		case 11  : return PIKGetIntProperty(message,reply,CPhidget_getDeviceVersion);
		case 12  : return PIKGetIntProperty(message,reply,(IntPropertyFunction)CPhidgetInterfaceKit_getOutputCount);
		case 13  : return PIKGetIntProperty(message,reply,(IntPropertyFunction)CPhidgetInterfaceKit_getSensorCount);
		case 14  : return PIKGetIntProperty(message,reply,(IntPropertyFunction)CPhidgetInterfaceKit_getInputCount);
			
			/* playing with triggers :D */
		case 15 : return PIKSetTriggerHandler(message,reply,CPhidgetInterfaceKit_set_OnOutputChange_Handler,IKOutputChanged);
		case 16 : return PIKSetTriggerHandler(message,reply,CPhidgetInterfaceKit_set_OnInputChange_Handler,IKInputChanged);
		case 17 : return PIKSetTriggerHandler(message,reply,CPhidgetInterfaceKit_set_OnSensorChange_Handler,IKSensorChanged);
			
		case 18 : return PIKSetRatiometric(message,reply);
			
		default:return errAEEventNotHandled;
    }
}
